import argparse
import logging
import shutil
import subprocess
import textwrap
from pathlib import Path

try:
    from pyodide_build.build_env import (
        get_build_flag,
        get_unisolated_packages,
    )
    from pyodide_build.recipe import load_all_recipes
except ImportError:
    print("Requires pyodide-build package to be installed")
    exit(1)


def _copy_xbuild_files(
    pyodide_root: Path, xbuildenv_path: Path, skip_missing_files: bool = False
) -> None:
    site_packages = Path(get_build_flag("HOSTSITEPACKAGES"))
    # Store package cross-build-files into site_packages_extras in the same tree
    # structure as they would appear in the real package.
    # In install_xbuildenv, we will use:
    # pip install -t $HOSTSITEPACKAGES -r requirements.txt
    # cp site-packages-extras $HOSTSITEPACKAGES
    site_packages_extras = xbuildenv_path / "site-packages-extras"
    recipes = load_all_recipes(pyodide_root / "packages")
    for recipe in recipes.values():
        xbuild_files = recipe.build.cross_build_files
        for path in xbuild_files:
            source = site_packages / path
            target = site_packages_extras / path
            target.parent.mkdir(parents=True, exist_ok=True)

            if not source.exists():
                if skip_missing_files:
                    logging.warning(f"Cross-build file '{path}' not found")
                    continue

                raise FileNotFoundError(f"Cross-build file '{path}' not found")

            shutil.copy(source, target)


def _copy_wasm_libs(
    pyodide_root: Path, xbuildenv_root: Path, skip_missing_files: bool = False
) -> None:
    def get_relative_path(pyodide_root: Path, flag: str) -> Path:
        return Path(get_build_flag(flag)).relative_to(pyodide_root)

    pythoninclude = get_relative_path(pyodide_root, "PYTHONINCLUDE")
    sysconfig_dir = get_relative_path(pyodide_root, "SYSCONFIGDATA_DIR")

    to_copy: list[Path] = [
        pythoninclude,
        sysconfig_dir,
        Path("Makefile.envs"),
        Path("dist/pyodide-lock.json"),
        Path("dist/python"),
        Path("dist/python_stdlib.zip"),
        Path("tools/constraints.txt"),
    ]
    to_copy.extend(
        x.relative_to(pyodide_root) for x in (pyodide_root / "dist").glob("pyodide.*")
    )

    for path in to_copy:
        if not (pyodide_root / path).exists():
            if skip_missing_files:
                logging.warning(f"Cross-build file '{path}' not found")
                continue

            raise FileNotFoundError(f"Cross-build file '{path}' not found")

        if (pyodide_root / path).is_dir():
            shutil.copytree(
                pyodide_root / path, xbuildenv_root / path, dirs_exist_ok=True
            )
        else:
            (xbuildenv_root / path).parent.mkdir(exist_ok=True, parents=True)
            shutil.copy(pyodide_root / path, xbuildenv_root / path)


def create(
    path: str | Path,
    pyodide_root: Path,
    *,
    skip_missing_files: bool = False,
) -> None:
    xbuildenv_path = Path(path) / "xbuildenv"
    xbuildenv_root = xbuildenv_path / "pyodide-root"

    shutil.rmtree(xbuildenv_path, ignore_errors=True)
    xbuildenv_path.mkdir(parents=True, exist_ok=True)
    xbuildenv_root.mkdir()

    _copy_xbuild_files(pyodide_root, xbuildenv_path, skip_missing_files)
    _copy_wasm_libs(pyodide_root, xbuildenv_root, skip_missing_files)

    (xbuildenv_root / "package.json").write_text("{}")
    res = subprocess.run(
        ["pip", "freeze", "--path", get_build_flag("HOSTSITEPACKAGES")],
        capture_output=True,
        encoding="utf8",
    )
    if res.returncode != 0:
        logging.error("Failed to run pip freeze:")
        if res.stdout:
            logging.error("  stdout:")
            logging.error(textwrap.indent(res.stdout, "    "))
        if res.stderr:
            logging.error("  stderr:")
            logging.error(textwrap.indent(res.stderr, "    "))
        exit(1)

    (xbuildenv_path / "requirements.txt").write_text(res.stdout)
    (xbuildenv_root / "unisolated.txt").write_text("\n".join(get_unisolated_packages()))


def parse_args():
    parser = argparse.ArgumentParser(
        description="Create cross-build environment for building packages for Pyodide."
    )
    parser.add_argument("path", help="path to cross-build environment directory")
    parser.add_argument(
        "--skip-missing-files",
        action="store_true",
        help="skip if cross build files are missing instead of raising an error. This is useful for testing.",
    )

    args = parser.parse_args()
    return args


def main():
    args = parse_args()
    root = Path(__file__).parent.parent

    create(args.path, pyodide_root=root, skip_missing_files=args.skip_missing_files)

    print(f"Pyodide cross-build environment created at {args.path}")


if __name__ == "__main__":
    main()
